﻿namespace MicroCloud.Identity
{
    /// <summary>
    /// 用户存储基类
    /// </summary>
    /// <typeparam name="TUser">用户类型</typeparam>
    /// <typeparam name="TUserKey">用户编号类型</typeparam>
    /// <typeparam name="TUserClaim">用户声明类型</typeparam>
    /// <typeparam name="TUserClaimKey">用户声明编号类型</typeparam>
    /// <typeparam name="TUserLogin">用户登录类型</typeparam>
    /// <typeparam name="TUserLoginKey">用户登录编号类型</typeparam>
    /// <typeparam name="TUserToken">用户令牌类型</typeparam>
    /// <typeparam name="TUserTokenKey">用户令牌编号类型</typeparam>
    /// <typeparam name="TRole">角色类型</typeparam>
    /// <typeparam name="TRoleKey">角色编号类型</typeparam>
    /// <typeparam name="TUserRole">用户角色类型</typeparam>
    /// <typeparam name="TUserRoleKey">用户角色编号类型</typeparam>
    /// <typeparam name="TOrganization">组织机构类型</typeparam>
    /// <typeparam name="TOrganizationKey">组织机构编号类型</typeparam>
    /// <typeparam name="TUserOrganization">用户组织机构类型</typeparam>
    /// <typeparam name="TUserOrganizationKey">用户组织机构编号类型</typeparam>
    public abstract class UserStoreBase<TUser, TUserKey, TUserClaim, TUserClaimKey, TUserLogin, TUserLoginKey, TUserToken, TUserTokenKey, TRole, TRoleKey, TUserRole, TUserRoleKey, TOrganization, TOrganizationKey, TUserOrganization, TUserOrganizationKey>
        : Disposable, IQueryableUserStore<TUser>,
          IUserLoginStore<TUser>,
          IUserClaimStore<TUser>,
          IUserPasswordStore<TUser>,
          IUserSecurityStampStore<TUser>,
          IUserEmailStore<TUser>,
          IUserLockoutStore<TUser>,
          IUserPhoneNumberStore<TUser>,
          IUserTwoFactorStore<TUser>,
          IMicroCloudUserAuthenticationTokenStore<TUser>,
          IUserAuthenticatorKeyStore<TUser>,
          IUserTwoFactorRecoveryCodeStore<TUser>,
          IUserRoleStore<TUser>,
          IUserOrganizationStore<TUser>
        where TUser : UserBase<TUserKey>
        where TUserKey : struct, IEquatable<TUserKey>
        where TUserClaim : UserClaimBase<TUserClaimKey, TUserKey>, new()
        where TUserClaimKey : struct, IEquatable<TUserClaimKey>
        where TUserLogin : UserLoginBase<TUserLoginKey, TUserKey>, new()
        where TUserLoginKey : struct, IEquatable<TUserLoginKey>
        where TUserToken : UserTokenBase<TUserTokenKey, TUserKey>, new()
        where TUserTokenKey : struct, IEquatable<TUserTokenKey>
        where TRole : RoleBase<TRoleKey, TUserKey>
        where TRoleKey : struct, IEquatable<TRoleKey>
        where TUserRole : UserRoleBase<TUserRoleKey, TUserKey, TRoleKey>, new()
        where TUserRoleKey : struct, IEquatable<TUserRoleKey>
        where TOrganization : OrganizationBase<TOrganizationKey, TUserKey>
        where TOrganizationKey : struct, IEquatable<TOrganizationKey>
        where TUserOrganization : UserOrganizationBase<TUserOrganizationKey, TUserKey, TOrganizationKey>, new()
        where TUserOrganizationKey : struct, IEquatable<TUserOrganizationKey>
    {
        //字段
        private readonly IRepository<TUser, TUserKey> _userRepository;
        private readonly IRepository<TUserLogin, TUserLoginKey> _userLoginRepository;
        private readonly IRepository<TUserClaim, TUserClaimKey> _userClaimRepository;
        private readonly IRepository<TUserToken, TUserTokenKey> _userTokenRepository;
        private readonly IRepository<TRole, TRoleKey> _roleRepository;
        private readonly IRepository<TUserRole, TUserRoleKey> _userRoleRepository;
        private readonly IRepository<TOrganization, TOrganizationKey> _organizationRepository;
        private readonly IRepository<TUserOrganization, TUserOrganizationKey> _userOrganizationRepository;
        private readonly IEventBus _eventBus;

        #region "属性"
        #region "获取 角色集合"
        /// <summary>
        /// 获取 角色集合
        /// </summary>
        protected IQueryable<TRole> Roles => _roleRepository.QueryAsNoTracking();
        #endregion
        #region "获取 组织机构集合"
        /// <summary>
        /// 获取 组织机构集合
        /// </summary>
        protected IQueryable<TOrganization> Organizations => _organizationRepository.QueryAsNoTracking();
        #endregion

        #endregion

        #region "属性 - Implementation of IQueryableUserStore<TUser>"
        #region "获取 用户集合"
        /// <summary>
        /// 获取 用户集合
        /// </summary>
        public IQueryable<TUser> Users => _userRepository.QueryAsNoTracking();
        #endregion

        #endregion

        #region "构造函数"
        #region "初始化一个用户存储基类的新实例"
        /// <summary>
        /// 初始化一个用户存储基类 <see cref="UserStoreBase{TUser, TUserKey, TUserClaim, TUserClaimKey, TUserLogin, TUserLoginKey, TUserToken, TUserTokenKey, TRole, TRoleKey, TUserRole, TUserRoleKey, TOrganization, TOrganizationKey,TUserOrg,TUserOrgKey}"/> 的新实例
        /// </summary>
        /// <param name="userRepository">用户仓储</param>
        /// <param name="userLoginRepository">用户登录仓储</param>
        /// <param name="userClaimRepository">用户声明仓储</param>
        /// <param name="userTokenRepository">用户令牌仓储</param>
        /// <param name="roleRepository">角色仓储</param>
        /// <param name="userRoleRepository">用户角色仓储</param>
        /// <param name="organizationRepository">组织机构仓储</param>
        /// <param name="userOrganizationRepository">用户组织机构仓储</param>
        /// <param name="eventBus">事件总线</param>
        protected UserStoreBase(
            IRepository<TUser, TUserKey> userRepository,
            IRepository<TUserLogin, TUserLoginKey> userLoginRepository,
            IRepository<TUserClaim, TUserClaimKey> userClaimRepository,
            IRepository<TUserToken, TUserTokenKey> userTokenRepository,
            IRepository<TRole, TRoleKey> roleRepository,
            IRepository<TUserRole, TUserRoleKey> userRoleRepository,
            IRepository<TOrganization, TOrganizationKey> organizationRepository,
            IRepository<TUserOrganization, TUserOrganizationKey> userOrganizationRepository,
            IEventBus eventBus
        )
        {
            _userRepository = userRepository;
            _userLoginRepository = userLoginRepository;
            _userClaimRepository = userClaimRepository;
            _userTokenRepository = userTokenRepository;
            _roleRepository = roleRepository;
            _userRoleRepository = userRoleRepository;
            _organizationRepository = organizationRepository;
            _userOrganizationRepository = userOrganizationRepository;
            _eventBus = eventBus;
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserStore<TUser>"
        #region "Gets the user identifier for the specified <paramref name="user" />."
        /// <summary>
        /// Gets the user identifier for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose identifier should be retrieved.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the identifier for the specified <paramref name="user" />.</returns>
        public virtual async Task<string> GetUserIdAsync(TUser user, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(ConvertIdToString(user.Id));
        }
        #endregion
        #region "Gets the user name for the specified <paramref name="user" />."
        /// <summary>
        /// Gets the user name for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose name should be retrieved.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the name for the specified <paramref name="user" />.</returns>
        public virtual async Task<string> GetUserNameAsync(TUser user, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(user.UserName);
        }
        #endregion
        #region "Sets the given <paramref name="userName" /> for the specified <paramref name="user" />."
        /// <summary>
        /// Sets the given <paramref name="userName" /> for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose name should be set.</param>
        /// <param name="userName">The user name to set.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            user.UserName = userName;
            await Task.CompletedTask;
        }
        #endregion
        #region "Gets the normalized user name for the specified <paramref name="user" />."
        /// <summary>
        /// Gets the normalized user name for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose normalized name should be retrieved.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the normalized user name for the specified <paramref name="user" />.</returns>
        public virtual async Task<string> GetNormalizedUserNameAsync(TUser user, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(user.NormalizedUserName);
        }
        #endregion
        #region "Sets the given normalized name for the specified <paramref name="user" />."
        /// <summary>
        /// Sets the given normalized name for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose name should be set.</param>
        /// <param name="normalizedName">The normalized name to set.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task SetNormalizedUserNameAsync(TUser user, string normalizedName, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            user.NormalizedUserName = normalizedName;
            await Task.CompletedTask;
        }
        #endregion
        #region "Creates the specified <paramref name="user" /> in the user store."
        /// <summary>
        /// Creates the specified <paramref name="user" /> in the user store.
        /// </summary>
        /// <param name="user">The user to create.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the <see cref="T:Microsoft.AspNetCore.Identity.IdentityResult" /> of the creation operation.</returns>
        public virtual async Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            //系统的第一个用户，自动成为系统用户
            var firstUser = _userRepository.QueryAsNoTracking().Any() == false;
            if (firstUser)
            {
                user.IsSystem = true;
            }

            var operationResult = await _userRepository.InsertAsync(user);
            if (operationResult.Succeeded)
            {
                //系统的第一个用户，自动成为超级管理员
                if (firstUser)
                {
                    TRole adminRole = _roleRepository.QueryAsNoTracking(o => o.IsAdmin && o.IsSystem).FirstOrDefault();
                    if (adminRole != null)
                    {
                        TUserRole userRole = new() { UserId = user.Id, RoleId = adminRole.Id };
                        await _userRoleRepository.InsertAsync(userRole);
                    }
                }
                //默认角色
                TRole defaultRole = Roles.FirstOrDefault(m => m.IsDefault);
                if (defaultRole != null)
                {
                    if (defaultRole.IsLocked)
                    {
                        throw new Exception(I18N.T("角色 {0}-{1} 已锁定", defaultRole.Name, defaultRole.Description));
                    }

                    TUserRole userRole = new() { UserId = user.Id, RoleId = defaultRole.Id };
                    await _userRoleRepository.InsertAsync(userRole);
                }
                //默认组织
                TOrganization defaultOrganizationEntity = Organizations.FirstOrDefault(m => m.IsDefault && !m.IsLocked);
                if (defaultOrganizationEntity != null)
                {
                    TUserOrganization userOrganization = new() { UserId = user.Id, OrganizationId = defaultOrganizationEntity.Id };
                    //组织绑定的第一个用户，自动成为系统管理员
                    var userOrgCount = _userOrganizationRepository.QueryAsNoTracking().Count();
                    if (userOrgCount == 0)
                    {
                        userOrganization.Administrator = true;
                    }
                    await _userOrganizationRepository.InsertAsync(userOrganization);
                }

                //触发创建用户事件
                EntityEventData<TUser> createUserEventData = new(_roleRepository.DbContext, OperateType.Insert) { Entities = new List<TUser> { user } };
                await _eventBus?.PublishAsync(createUserEventData);

                return IdentityResult.Success;
            }
            return new IdentityResult().Failed(operationResult.Message);
        }
        #endregion
        #region "Updates the specified <paramref name="user" /> in the user store."
        /// <summary>
        /// Updates the specified <paramref name="user" /> in the user store.
        /// </summary>
        /// <param name="user">The user to update.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the <see cref="T:Microsoft.AspNetCore.Identity.IdentityResult" /> of the update operation.</returns>
        public virtual async Task<IdentityResult> UpdateAsync(TUser user, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            if (string.IsNullOrEmpty(user.Email))
            {
                user.EmailConfirmed = false;
            }
            if (string.IsNullOrEmpty(user.PhoneNumber))
            {
                user.PhoneConfirmed = false;
            }

            var operationResult = await _userRepository.UpdateAsync(user);
            switch (operationResult.ResultType)
            {
                case OperationResultType.Success:
                    //移除用户在线缓存
                    OnlineUserCacheRemoveEventData eventData = new() { UserIds = new[] { user.Id.ToString() } };
                    await _eventBus?.PublishAsync(eventData);

                    //触发更新用户事件
                    EntityEventData<TUser> updateUserEventData = new(_userRepository.DbContext, OperateType.Update) { Entities = new List<TUser> { user } };
                    await _eventBus?.PublishAsync(updateUserEventData);

                    return IdentityResult.Success;
            }

            return new IdentityResult().Failed(operationResult.Message);
        }
        #endregion
        #region "Deletes the specified <paramref name="user" /> from the user store."
        /// <summary>
        /// Deletes the specified <paramref name="user" /> from the user store.
        /// </summary>
        /// <param name="user">The user to delete.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the <see cref="T:Microsoft.AspNetCore.Identity.IdentityResult" /> of the update operation.</returns>
        public virtual async Task<IdentityResult> DeleteAsync(TUser user, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            if (user.IsSystem)
            {
                return new IdentityResult().Failed(I18N.T("不能删除系统用户 {0}", user.UserName));
            }

            var operationResult = await _userRepository.DeleteAsync(user);
            if (operationResult.Succeeded)
            {
                //触发删除用户事件
                EntityEventData<TUser> deleteUserEventData = new(_userRepository.DbContext, OperateType.Delete) { Entities = new List<TUser> { user } };
                await _eventBus?.PublishAsync(deleteUserEventData);

                return IdentityResult.Success;
            }
            return new IdentityResult().Failed(operationResult.Message);
        }
        #endregion
        #region "Finds and returns a user, if any, who has the specified <paramref name="userId" />."
        /// <summary>
        /// Finds and returns a user, if any, who has the specified <paramref name="userId" />.
        /// </summary>
        /// <param name="userId">The user ID to search for.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the user matching the specified <paramref name="userId" /> if it exists.
        /// </returns>
        public virtual async Task<TUser> FindByIdAsync(string userId, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();

            TUserKey id = ConvertIdFromString(userId);
            return await Task.FromResult(_userRepository.Get(id));
        }
        #endregion
        #region "Finds and returns a user, if any, who has the specified normalized user name."
        /// <summary>
        /// Finds and returns a user, if any, who has the specified normalized user name.
        /// </summary>
        /// <param name="normalizedUserName">The normalized user name to search for.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the user matching the specified <paramref name="normalizedUserName" /> if it exists.
        /// </returns>
        public virtual async Task<TUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();

            return await Task.FromResult(Users.FirstOrDefault(m => m.NormalizedUserName == normalizedUserName));
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserLoginStore<TUser>"
        #region "Adds an external <see cref="T:Microsoft.AspNetCore.Identity.UserLoginInfo" /> to the specified <paramref name="user" />."
        /// <summary>
        /// Adds an external <see cref="T:Microsoft.AspNetCore.Identity.UserLoginInfo" /> to the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user to add the login to.</param>
        /// <param name="login">The external <see cref="T:Microsoft.AspNetCore.Identity.UserLoginInfo" /> to add to the specified <paramref name="user" />.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));
            Check.NotNull(login, nameof(login));

            TUserLogin userLogin = new()
            {
                UserId = user.Id,
                LoginProvider = login.LoginProvider,
                ProviderKey = login.ProviderKey,
                DisplayName = login.ProviderDisplayName,
            };
            await _userLoginRepository.InsertAsync(userLogin);
        }
        #endregion
        #region "Attempts to remove the provided login information from the specified <paramref name="user" />."
        /// <summary>
        /// Attempts to remove the provided login information from the specified <paramref name="user" />.
        /// and returns a flag indicating whether the removal succeed or not.
        /// </summary>
        /// <param name="user">The user to remove the login information from.</param>
        /// <param name="loginProvider">The login provide whose information should be removed.</param>
        /// <param name="providerKey">The key given by the external login provider for the specified user.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));
            Check.NotNullOrEmpty(loginProvider, nameof(loginProvider));
            Check.NotNullOrEmpty(providerKey, nameof(providerKey));

            await _userLoginRepository.DeleteBatchAsync(m => m.UserId.Equals(user.Id) && m.LoginProvider == loginProvider && m.ProviderKey == providerKey);
        }
        #endregion
        #region "Retrieves the associated logins for the specified <param ref="user" />."
        /// <summary>
        /// Retrieves the associated logins for the specified <param ref="user" />.
        /// </summary>
        /// <param name="user">The user whose associated logins to retrieve.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// The <see cref="T:System.Threading.Tasks.Task" /> for the asynchronous operation, containing a list of <see cref="T:Microsoft.AspNetCore.Identity.UserLoginInfo" /> for the specified <paramref name="user" />, if any.
        /// </returns>
        public virtual async Task<IList<UserLoginInfo>> GetLoginsAsync(TUser user, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            IList<UserLoginInfo> loginInfos = _userLoginRepository.QueryAsNoTracking(m => m.UserId.Equals(user.Id)).Select(m => new UserLoginInfo(m.LoginProvider, m.ProviderKey, m.DisplayName)).ToList();
            return await Task.FromResult(loginInfos);
        }
        #endregion
        #region "Retrieves the user associated with the specified login provider and login provider key."
        /// <summary>
        /// Retrieves the user associated with the specified login provider and login provider key.
        /// </summary>
        /// <param name="loginProvider">The login provider who provided the <paramref name="providerKey" />.</param>
        /// <param name="providerKey">The key provided by the <paramref name="loginProvider" /> to identify a user.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// The <see cref="T:System.Threading.Tasks.Task" /> for the asynchronous operation, containing the user, if any which matched the specified login provider and key.
        /// </returns>
        public virtual async Task<TUser> FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNullOrEmpty(loginProvider, nameof(loginProvider));
            Check.NotNullOrEmpty(providerKey, nameof(providerKey));

            TUserKey userId = _userLoginRepository.QueryAsNoTracking(m => m.LoginProvider == loginProvider && m.ProviderKey == providerKey).Select(m => m.UserId).FirstOrDefault();
            if (Equals(userId, default(TUserKey)))
            {
                return await Task.FromResult(default(TUser));
            }
            TUser user = _userRepository.Get(userId);
            return await Task.FromResult(user);
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserClaimStore<TUser>"
        #region "Gets a list of <see cref="T:System.Security.Claims.Claim" />s to be belonging to the specified <paramref name="user" /> as an asynchronous operation."
        /// <summary>
        /// Gets a list of <see cref="T:System.Security.Claims.Claim" />s to be belonging to the specified <paramref name="user" /> as an asynchronous operation.
        /// </summary>
        /// <param name="user">The role whose claims to retrieve.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// A <see cref="T:System.Threading.Tasks.Task`1" /> that represents the result of the asynchronous query, a list of <see cref="T:System.Security.Claims.Claim" />s.
        /// </returns>
        public virtual async Task<IList<Claim>> GetClaimsAsync(TUser user, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            IList<Claim> claims = _userClaimRepository.QueryAsNoTracking(m => m.UserId.Equals(user.Id))
                .Select(m => new Claim(m.ClaimType, m.ClaimValue)).ToList();
            return await Task.FromResult(claims);
        }
        #endregion
        #region "Add claims to a user as an asynchronous operation."
        /// <summary>Add claims to a user as an asynchronous operation.</summary>
        /// <param name="user">The user to add the claim to.</param>
        /// <param name="claims">The collection of <see cref="T:System.Security.Claims.Claim" />s to add.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The task object representing the asynchronous operation.</returns>
        public virtual async Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            TUserClaim[] userClaims = claims.Select(m => new TUserClaim() { UserId = user.Id, ClaimType = m.Type, ClaimValue = m.Value }).ToArray();
            await _userClaimRepository.InsertAsync(userClaims);
        }
        #endregion
        #region "Replaces the given <paramref name="claim" /> on the specified <paramref name="user" /> with the <paramref name="newClaim" />"
        /// <summary>
        /// Replaces the given <paramref name="claim" /> on the specified <paramref name="user" /> with the <paramref name="newClaim" />
        /// </summary>
        /// <param name="user">The user to replace the claim on.</param>
        /// <param name="claim">The claim to replace.</param>
        /// <param name="newClaim">The new claim to replace the existing <paramref name="claim" /> with.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The task object representing the asynchronous operation.</returns>
        public virtual async Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            List<TUserClaim> userClaims = _userClaimRepository.QueryAsNoTracking(m => m.UserId.Equals(user.Id) && m.ClaimType == claim.Type && m.ClaimValue == claim.Value).ToList();
            foreach (TUserClaim userClaim in userClaims)
            {
                userClaim.ClaimType = newClaim.Type;
                userClaim.ClaimValue = newClaim.Value;
            }
            await Task.CompletedTask;
        }
        #endregion
        #region "Removes the specified <paramref name="claims" /> from the given <paramref name="user" />."
        /// <summary>
        /// Removes the specified <paramref name="claims" /> from the given <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user to remove the specified <paramref name="claims" /> from.</param>
        /// <param name="claims">A collection of <see cref="T:System.Security.Claims.Claim" />s to remove.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The task object representing the asynchronous operation.</returns>
        public virtual async Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            await _userClaimRepository.DeleteBatchAsync(m => m.UserId.Equals(user.Id) && claims.Any(n => n.Type == m.ClaimType && n.Value == m.ClaimValue));
        }
        #endregion
        #region "Returns a list of users who contain the specified <see cref="T:System.Security.Claims.Claim" />."
        /// <summary>
        /// Returns a list of users who contain the specified <see cref="T:System.Security.Claims.Claim" />.
        /// </summary>
        /// <param name="claim">The claim to look for.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// A <see cref="T:System.Threading.Tasks.Task`1" /> that represents the result of the asynchronous query, a list of <typeparamref name="TUser" /> who
        /// contain the specified claim.
        /// </returns>
        public virtual async Task<IList<TUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(claim, nameof(claim));

            TUserKey[] userIds = _userClaimRepository.QueryAsNoTracking(m => m.ClaimType == claim.Type && m.ClaimValue == claim.Value).Select(m => m.UserId).ToArray();
            IList<TUser> users = _userRepository.QueryAsNoTracking(m => userIds.Contains(m.Id)).ToList();
            return await Task.FromResult(users);
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserPasswordStore<TUser>"
        #region "Sets the password hash for the specified <paramref name="user" />."
        /// <summary>
        /// Sets the password hash for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose password hash to set.</param>
        /// <param name="passwordHash">The password hash to set.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            user.PasswordHash = passwordHash;
            await Task.CompletedTask;
        }
        #endregion
        #region "Gets the password hash for the specified <paramref name="user" />."
        /// <summary>
        /// Gets the password hash for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose password hash to retrieve.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, returning the password hash for the specified <paramref name="user" />.</returns>
        public virtual async Task<string> GetPasswordHashAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(user.PasswordHash);
        }
        #endregion
        #region "Gets a flag indicating whether the specified <paramref name="user" /> has a password."
        /// <summary>
        /// Gets a flag indicating whether the specified <paramref name="user" /> has a password.
        /// </summary>
        /// <param name="user">The user to return a flag for, indicating whether they have a password or not.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, returning true if the specified <paramref name="user" /> has a password
        /// otherwise false.
        /// </returns>
        public virtual async Task<bool> HasPasswordAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(!string.IsNullOrEmpty(user.PasswordHash));
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserSecurityStampStore<TUser>"
        #region "Sets the provided security <paramref name="stamp" /> for the specified <paramref name="user" />."
        /// <summary>
        /// Sets the provided security <paramref name="stamp" /> for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose security stamp should be set.</param>
        /// <param name="stamp">The security stamp to set.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            user.SecurityStamp = stamp;

            await Task.CompletedTask;
        }
        #endregion
        #region "Get the security stamp for the specified <paramref name="user" />."
        /// <summary>
        /// Get the security stamp for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose security stamp should be set.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the security stamp for the specified <paramref name="user" />.</returns>
        public virtual async Task<string> GetSecurityStampAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(user.SecurityStamp);
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserEmailStore<TUser>"
        #region "Sets the <paramref name="email" /> address for a <paramref name="user" />."
        /// <summary>
        /// Sets the <paramref name="email" /> address for a <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose email should be set.</param>
        /// <param name="email">The email to set.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The task object representing the asynchronous operation.</returns>
        public virtual async Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));
            Check.NotNull(email, nameof(email));

            user.Email = email;
            await Task.CompletedTask;
        }
        #endregion
        #region "Gets the email address for the specified <paramref name="user" />."
        /// <summary>
        /// Gets the email address for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose email should be returned.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The task object containing the results of the asynchronous operation, the email address for the specified <paramref name="user" />.</returns>
        public virtual async Task<string> GetEmailAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(user.Email);
        }
        #endregion
        #region "Gets a flag indicating whether the email address for the specified <paramref name="user" /> has been verified, true if the email address is verified otherwise"
        /// <summary>
        /// Gets a flag indicating whether the email address for the specified <paramref name="user" /> has been verified, true if the email address is verified otherwise
        /// false.
        /// </summary>
        /// <param name="user">The user whose email confirmation status should be returned.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// The task object containing the results of the asynchronous operation, a flag indicating whether the email address for the specified <paramref name="user" />
        /// has been confirmed or not.
        /// </returns>
        public virtual async Task<bool> GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(user.EmailConfirmed);
        }
        #endregion
        #region "Sets the flag indicating whether the specified <paramref name="user" />'s email address has been confirmed or not."
        /// <summary>
        /// Sets the flag indicating whether the specified <paramref name="user" />'s email address has been confirmed or not.
        /// </summary>
        /// <param name="user">The user whose email confirmation status should be set.</param>
        /// <param name="confirmed">A flag indicating if the email address has been confirmed, true if the address is confirmed otherwise false.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The task object representing the asynchronous operation.</returns>
        public virtual async Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            user.EmailConfirmed = true;
            await Task.CompletedTask;
        }
        #endregion
        #region "Gets the user, if any, associated with the specified, normalized email address."
        /// <summary>
        /// Gets the user, if any, associated with the specified, normalized email address.
        /// </summary>
        /// <param name="normalizedEmail">The normalized email address to return the user for.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// The task object containing the results of the asynchronous lookup operation, the user if any associated with the specified normalized email address.
        /// </returns>
        public virtual async Task<TUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();

            TUser user = Users.FirstOrDefault(m => m.NormalizeEmail == normalizedEmail);
            return await Task.FromResult(user);
        }
        #endregion
        #region "Returns the normalized email for the specified <paramref name="user" />."
        /// <summary>
        /// Returns the normalized email for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose email address to retrieve.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// The task object containing the results of the asynchronous lookup operation, the normalized email address if any associated with the specified user.
        /// </returns>
        public virtual async Task<string> GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(user.NormalizedUserName);
        }
        #endregion
        #region "Sets the normalized email for the specified <paramref name="user" />."
        /// <summary>
        /// Sets the normalized email for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose email address to set.</param>
        /// <param name="normalizedEmail">The normalized email to set for the specified <paramref name="user" />.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The task object representing the asynchronous operation.</returns>
        public virtual async Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            user.NormalizeEmail = normalizedEmail;
            await Task.CompletedTask;
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserLockoutStore<TUser>"
        #region "Gets the last <see cref="T:System.DateTimeOffset" /> a user's last lockout expired, if any."
        /// <summary>
        /// Gets the last <see cref="T:System.DateTimeOffset" /> a user's last lockout expired, if any.
        /// Any time in the past should be indicates a user is not locked out.
        /// </summary>
        /// <param name="user">The user whose lockout date should be retrieved.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// A <see cref="T:System.Threading.Tasks.Task`1" /> that represents the result of the asynchronous query, a <see cref="T:System.DateTimeOffset" /> containing the last time
        /// a user's lockout expired, if any.
        /// </returns>
        public virtual async Task<DateTimeOffset?> GetLockoutEndDateAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(user.LockoutEnd);
        }
        #endregion
        #region "Locks out a user until the specified end date has passed. Setting a end date in the past immediately unlocks a user."
        /// <summary>
        /// Locks out a user until the specified end date has passed. Setting a end date in the past immediately unlocks a user.
        /// </summary>
        /// <param name="user">The user whose lockout date should be set.</param>
        /// <param name="lockoutEnd">The <see cref="T:System.DateTimeOffset" /> after which the <paramref name="user" />'s lockout should end.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            user.LockoutEnd = lockoutEnd;
            await Task.CompletedTask;
        }
        #endregion
        #region "Records that a failed access has occurred, incrementing the failed access count."
        /// <summary>
        /// Records that a failed access has occurred, incrementing the failed access count.
        /// </summary>
        /// <param name="user">The user whose cancellation count should be incremented.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the incremented failed access count.</returns>
        public virtual async Task<int> IncrementAccessFailedCountAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            user.AccessFailedCount++;
            return await Task.FromResult(user.AccessFailedCount);
        }
        #endregion
        #region "Resets a user's failed access count."
        /// <summary>Resets a user's failed access count.</summary>
        /// <param name="user">The user whose failed access count should be reset.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        /// <remarks>This is typically called after the account is successfully accessed.</remarks>
        public virtual async Task ResetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            user.AccessFailedCount = 0;
            await Task.CompletedTask;
        }
        #endregion
        #region "Retrieves the current failed access count for the specified <paramref name="user" />."
        /// <summary>
        /// Retrieves the current failed access count for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose failed access count should be retrieved.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the failed access count.</returns>
        public virtual async Task<int> GetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(user.AccessFailedCount);
        }
        #endregion
        #region "Retrieves a flag indicating whether user lockout can enabled for the specified user."
        /// <summary>
        /// Retrieves a flag indicating whether user lockout can enabled for the specified user.
        /// </summary>
        /// <param name="user">The user whose ability to be locked out should be returned.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, true if a user can be locked out, otherwise false.
        /// </returns>
        public virtual async Task<bool> GetLockoutEnabledAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(user.LockoutEnabled);
        }
        #endregion
        #region "Set the flag indicating if the specified <paramref name="user" /> can be locked out."
        /// <summary>
        /// Set the flag indicating if the specified <paramref name="user" /> can be locked out.
        /// </summary>
        /// <param name="user">The user whose ability to be locked out should be set.</param>
        /// <param name="enabled">A flag indicating if lock out can be enabled for the specified <paramref name="user" />.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            user.LockoutEnabled = enabled;
            await Task.CompletedTask;
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserPhoneNumberStore<TUser>"
        #region "Sets the telephone number for the specified <paramref name="user" />."
        /// <summary>
        /// Sets the telephone number for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose telephone number should be set.</param>
        /// <param name="phoneNumber">The telephone number to set.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task SetPhoneNumberAsync(TUser user, string phoneNumber, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            user.PhoneNumber = phoneNumber;
            await Task.CompletedTask;
        }
        #endregion
        #region "Gets the telephone number, if any, for the specified <paramref name="user" />."
        /// <summary>
        /// Gets the telephone number, if any, for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose telephone number should be retrieved.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the user's telephone number, if any.</returns>
        public virtual async Task<string> GetPhoneNumberAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(user.PhoneNumber);
        }
        #endregion
        #region "Gets a flag indicating whether the specified <paramref name="user" />'s telephone number has been confirmed."
        /// <summary>
        /// Gets a flag indicating whether the specified <paramref name="user" />'s telephone number has been confirmed.
        /// </summary>
        /// <param name="user">The user to return a flag for, indicating whether their telephone number is confirmed.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, returning true if the specified <paramref name="user" /> has a confirmed
        /// telephone number otherwise false.
        /// </returns>
        public virtual async Task<bool> GetPhoneNumberConfirmedAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(user.PhoneConfirmed);
        }
        #endregion
        #region "Sets a flag indicating if the specified <paramref name="user" />'s phone number has been confirmed."
        /// <summary>
        /// Sets a flag indicating if the specified <paramref name="user" />'s phone number has been confirmed.
        /// </summary>
        /// <param name="user">The user whose telephone number confirmation status should be set.</param>
        /// <param name="confirmed">A flag indicating whether the user's telephone number has been confirmed.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            user.PhoneConfirmed = confirmed;
            await Task.CompletedTask;
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserTwoFactorStore<TUser>"
        #region "Sets a flag indicating whether the specified <paramref name="user" /> has two factor authentication enabled or not"
        /// <summary>
        /// Sets a flag indicating whether the specified <paramref name="user" /> has two factor authentication enabled or not,
        /// as an asynchronous operation.
        /// </summary>
        /// <param name="user">The user whose two factor authentication enabled status should be set.</param>
        /// <param name="enabled">A flag indicating whether the specified <paramref name="user" /> has two factor authentication enabled.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            user.TwoFactorEnabled = enabled;
            await Task.CompletedTask;
        }
        #endregion
        #region "Returns a flag indicating whether the specified <paramref name="user" /> has two factor authentication enabled or not"
        /// <summary>
        /// Returns a flag indicating whether the specified <paramref name="user" /> has two factor authentication enabled or not,
        /// as an asynchronous operation.
        /// </summary>
        /// <param name="user">The user whose two factor authentication enabled status should be set.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing a flag indicating whether the specified
        /// <paramref name="user" /> has two factor authentication enabled or not.
        /// </returns>
        public virtual async Task<bool> GetTwoFactorEnabledAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            return await Task.FromResult(user.TwoFactorEnabled);
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserAuthenticationTokenStore<TUser>"
        #region "Sets the token value for a particular user."
        /// <summary>Sets the token value for a particular user.</summary>
        /// <param name="user">The user.</param>
        /// <param name="loginProvider">The authentication provider for the token.</param>
        /// <param name="name">The name of the token.</param>
        /// <param name="value">The value of the token.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task SetTokenAsync(TUser user, string loginProvider, string name, string value, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));
            Check.NotNullOrEmpty(loginProvider, nameof(loginProvider));
            Check.NotNullOrEmpty(name, nameof(name));
            Check.NotNullOrEmpty(value, nameof(value));

            //var refreshToken = value.FromJsonString<RefreshToken>();

            TUserToken token = _userTokenRepository.QueryAsNoTracking(m => m.UserId.Equals(user.Id) && m.LoginProvider == loginProvider && m.Name == name, false).FirstOrDefault();
            if (token == null)
            {
                token = new TUserToken() { UserId = user.Id, LoginProvider = loginProvider, Name = name, Value = value };
                await _userTokenRepository.InsertAsync(token);
            }
            else
            {
                token.Value = value;
                await _userTokenRepository.UpdateAsync(token);
            }
        }
        #endregion
        #region "Deletes a token for a user."
        /// <summary>Deletes a token for a user.</summary>
        /// <param name="user">The user.</param>
        /// <param name="loginProvider">The authentication provider for the token.</param>
        /// <param name="name">The name of the token.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));
            Check.NotNullOrEmpty(loginProvider, nameof(loginProvider));
            Check.NotNullOrEmpty(name, nameof(name));

            await _userTokenRepository.DeleteBatchAsync(m => m.UserId.Equals(user.Id) && m.LoginProvider == loginProvider && m.Name == name);
        }
        #endregion
        #region "Returns the token value."
        /// <summary>Returns the token value.</summary>
        /// <param name="user">The user.</param>
        /// <param name="loginProvider">The authentication provider for the token.</param>
        /// <param name="name">The name of the token.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task<string> GetTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));
            Check.NotNullOrEmpty(loginProvider, nameof(loginProvider));
            Check.NotNullOrEmpty(name, nameof(name));

            string value = _userTokenRepository.QueryAsNoTracking(m => m.UserId.Equals(user.Id) && m.LoginProvider == loginProvider && m.Name == name).Select(m => m.Value).FirstOrDefault();
            return await Task.FromResult(value);
        }
        #endregion
        #region "获取某个用户的所有指定登录提供者的权限标识"
        /// <summary>
        /// 获取某个用户的所有指定登录提供者的权限标识
        /// </summary>
        /// <param name="user">用户信息</param>
        /// <param name="loginProvider">登录提供者</param>
        /// <param name="cancellationToken">任务取消标识</param>
        /// <returns>权限标识集合</returns>
        public virtual async Task<string[]> GetTokensAsync(TUser user, string loginProvider, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));
            Check.NotNullOrEmpty(loginProvider, nameof(loginProvider));

            string[] values = _userTokenRepository.QueryAsNoTracking(m => m.UserId.Equals(user.Id) && m.LoginProvider == loginProvider).Select(m => m.Value).ToArray();
            return await Task.FromResult(values);
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserAuthenticatorKeyStore<TUser>"
        //常量
        private const string InternalLoginProvider = "[AspNetUserStore]";
        private const string AuthenticatorKeyTokenName = "AuthenticatorKey";
        private const string RecoveryCodeTokenName = "RecoveryCodes";

        #region "Sets the authenticator key for the specified <paramref name="user" />."
        /// <summary>
        /// Sets the authenticator key for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose authenticator key should be set.</param>
        /// <param name="key">The authenticator key to set.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task SetAuthenticatorKeyAsync(TUser user, string key, CancellationToken cancellationToken)
        {
            await SetTokenAsync(user, InternalLoginProvider, AuthenticatorKeyTokenName, key, cancellationToken);
        }
        #endregion
        #region "Get the authenticator key for the specified <paramref name="user" />."
        /// <summary>
        /// Get the authenticator key for the specified <paramref name="user" />.
        /// </summary>
        /// <param name="user">The user whose security stamp should be set.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the security stamp for the specified <paramref name="user" />.</returns>
        public virtual async Task<string> GetAuthenticatorKeyAsync(TUser user, CancellationToken cancellationToken)
        {
            return await GetTokenAsync(user, InternalLoginProvider, AuthenticatorKeyTokenName, cancellationToken);
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserTwoFactorRecoveryCodeStore<TUser>"
        #region "Updates the recovery codes for the user while invalidating any previous recovery codes."
        /// <summary>
        /// Updates the recovery codes for the user while invalidating any previous recovery codes.
        /// </summary>
        /// <param name="user">The user to store new recovery codes for.</param>
        /// <param name="recoveryCodes">The new recovery codes for the user.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The new recovery codes for the user.</returns>
        public virtual async Task ReplaceCodesAsync(TUser user, IEnumerable<string> recoveryCodes, CancellationToken cancellationToken)
        {
            string mergedCodes = string.Join(";", recoveryCodes);
            await SetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, mergedCodes, cancellationToken);
        }
        #endregion
        #region "Returns whether a recovery code is valid for a user. Note: recovery codes are only valid once, and will be invalid after use."
        /// <summary>
        /// Returns whether a recovery code is valid for a user. Note: recovery codes are only valid
        /// once, and will be invalid after use.
        /// </summary>
        /// <param name="user">The user who owns the recovery code.</param>
        /// <param name="code">The recovery code to use.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>True if the recovery code was found for the user.</returns>
        public virtual async Task<bool> RedeemCodeAsync(TUser user, string code, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));
            Check.NotNullOrEmpty(code, nameof(code));

            string mergedCodes = await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? string.Empty;
            string[] splitCodes = mergedCodes.Split(';');
            if (splitCodes.Contains(code))
            {
                List<string> updatedCodes = new(splitCodes.Where(s => s != code));
                await ReplaceCodesAsync(user, updatedCodes, cancellationToken);
                return true;
            }
            return false;
        }
        #endregion
        #region "Returns how many recovery code are still valid for a user."
        /// <summary>
        /// Returns how many recovery code are still valid for a user.
        /// </summary>
        /// <param name="user">The user who owns the recovery code.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The number of valid recovery codes for the user..</returns>
        public virtual async Task<int> CountCodesAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            string mergedCodes = await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken);
            if (mergedCodes.Length > 0)
            {
                return mergedCodes.Split(';').Length;
            }
            return 0;
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserRoleStore<TUser>"
        #region "Add a the specified <paramref name="user" /> to the named role."
        /// <summary>
        /// Add a the specified <paramref name="user" /> to the named role.
        /// </summary>
        /// <param name="user">The user to add to the named role.</param>
        /// <param name="normalizedRoleName">The name of the role to add the user to.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task AddToRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));
            Check.NotNullOrEmpty(normalizedRoleName, nameof(normalizedRoleName));

            TRoleKey roleId = _roleRepository.QueryAsNoTracking(m => m.NormalizedName == normalizedRoleName).Select(m => m.Id).FirstOrDefault();
            if (Equals(roleId, default(TRoleKey)))
            {
                throw new InvalidOperationException(I18N.T("角色 {0} 不存在", normalizedRoleName));
            }
            TUserRole userRole = new() { RoleId = roleId, UserId = user.Id };
            await _userRoleRepository.InsertAsync(userRole);
        }
        #endregion
        #region "Add a the specified <paramref name="user" /> from the named role."
        /// <summary>
        /// Add a the specified <paramref name="user" /> from the named role.
        /// </summary>
        /// <param name="user">The user to remove the named role from.</param>
        /// <param name="normalizedRoleName">The name of the role to remove.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
        public virtual async Task RemoveFromRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));
            Check.NotNullOrEmpty(normalizedRoleName, nameof(normalizedRoleName));

            TRole role = _roleRepository.QueryAsNoTracking().FirstOrDefault(m => m.NormalizedName == normalizedRoleName);
            if (role == null)
            {
                throw new InvalidOperationException(I18N.T("角色 {0} 不存在", normalizedRoleName));
            }
            if (user.IsSystem && role.IsSystem)
            {
                throw new InvalidOperationException(I18N.T("系统用户 {0} 的系统角色 {1} 不能移除", user.UserName, role.Name));
            }
            await _userRoleRepository.DeleteBatchAsync(m => m.UserId.Equals(user.Id) && m.RoleId.Equals(role.Id));
        }
        #endregion
        #region "Gets a list of role names the specified <paramref name="user" /> belongs to."
        /// <summary>
        /// Gets a list of role names the specified <paramref name="user" /> belongs to.
        /// </summary>
        /// <param name="user">The user whose role names to retrieve.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing a list of role names.</returns>
        public virtual async Task<IList<string>> GetRolesAsync(TUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            IList<string> list = new List<string>();
            List<TRoleKey> roleIds = _userRoleRepository.QueryAsNoTracking(m => m.UserId.Equals(user.Id)).Select(m => m.RoleId).ToList();
            if (roleIds.Count == 0)
            {
                return await Task.FromResult(list);
            }
            list = _roleRepository.QueryAsNoTracking(m => roleIds.Contains(m.Id)).Select(m => m.Name).ToList();
            return await Task.FromResult(list);
        }
        #endregion
        #region "Returns a flag indicating whether the specified <paramref name="user" /> is a member of the give named role."
        /// <summary>
        /// Returns a flag indicating whether the specified <paramref name="user" /> is a member of the give named role.
        /// </summary>
        /// <param name="user">The user whose role membership should be checked.</param>
        /// <param name="roleName">The name of the role to be checked.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing a flag indicating whether the specified <paramref name="user" /> is
        /// a member of the named role.
        /// </returns>
        public virtual async Task<bool> IsInRoleAsync(TUser user, string roleName, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            TRoleKey roleId = _roleRepository.QueryAsNoTracking(m => m.NormalizedName == roleName).Select(m => m.Id).FirstOrDefault();
            if (Equals(roleId, default(TRoleKey)))
            {
                throw new InvalidOperationException(I18N.T("角色 {0} 不存在", roleName));
            }
            bool exist = _userRoleRepository.QueryAsNoTracking(m => m.UserId.Equals(user.Id) && m.RoleId.Equals(roleId)).Any();
            return await Task.FromResult(exist);
        }
        #endregion
        #region "Returns a list of Users who are members of the named role."
        /// <summary>
        /// Returns a list of Users who are members of the named role.
        /// </summary>
        /// <param name="roleName">The name of the role whose membership should be returned.</param>
        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken" /> used to propagate notifications that the operation should be canceled.</param>
        /// <returns>
        /// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing a list of users who are in the named role.
        /// </returns>
        public virtual async Task<IList<TUser>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNullOrEmpty(roleName, nameof(roleName));

            TRoleKey roleId = _roleRepository.QueryAsNoTracking(m => m.NormalizedName == roleName).Select(m => m.Id).FirstOrDefault();
            if (Equals(roleId, default(TRoleKey)))
            {
                throw new InvalidOperationException(I18N.T("角色 {0} 不存在", roleName));
            }
            List<TUserKey> userIds = _userRoleRepository.QueryAsNoTracking(m => m.RoleId.Equals(roleId)).Select(m => m.UserId).ToList();
            IList<TUser> users = _userRepository.QueryAsNoTracking(m => userIds.Contains(m.Id)).ToList();
            return await Task.FromResult(users);
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IUserOrganizationStore<TUser>"
        #region "将指定的用户添加到组织机构"
        /// <summary>
        /// 将指定的用户添加到组织机构
        /// </summary>
        /// <param name="user">用户数据</param>
        /// <param name="organizationId">组织机构编号</param>
        /// <param name="cancellationToken">用于传播应取消操作的通知</param>
        /// <returns><see cref="Task" />结果</returns>
        public virtual async Task AddToOrganizationAsync(TUser user, string organizationId, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));
            Check.NotNullOrEmpty(organizationId, nameof(organizationId));

            TOrganizationKey organizationKey = _organizationRepository.QueryAsNoTracking(m => Equals(m.Id, organizationId.CastTo<TOrganizationKey>())).Select(m => m.Id).FirstOrDefault();
            if (Equals(organizationKey, default(TOrganizationKey)))
            {
                throw new InvalidOperationException(I18N.T("组织机构 {0} 不存在", organizationId));
            }
            TUserOrganization userOrganization = new() { OrganizationId = organizationKey, UserId = user.Id };
            await _userOrganizationRepository.InsertAsync(userOrganization);
        }
        #endregion
        #region "从组织机构中删除指定的用户"
        /// <summary>
        /// 从组织机构中删除指定的用户
        /// </summary>
        /// <param name="user">用户数据</param>
        /// <param name="organizationId">组织机构编号</param>
        /// <param name="cancellationToken">用于传播应取消操作的通知</param>
        /// <returns><see cref="Task" />结果</returns>
        public virtual async Task RemoveFromOrganizationAsync(TUser user, string organizationId, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));
            Check.NotNullOrEmpty(organizationId, nameof(organizationId));

            TOrganization organization = _organizationRepository.QueryAsNoTracking().FirstOrDefault(m => Equals(m.Id, organizationId.CastTo<TOrganizationKey>())) ?? throw new InvalidOperationException(I18N.T("组织机构 {0} 不存在", organizationId));
            if (user.IsSystem && organization.IsSystem)
            {
                throw new InvalidOperationException(I18N.T("系统用户 {0} 的系统组织机构 {1} 不能移除", user.UserName, organization.Name));
            }
            await _userOrganizationRepository.DeleteBatchAsync(m => m.UserId.Equals(user.Id) && m.OrganizationId.Equals(organization.Id));
        }
        #endregion
        #region "获取指定用户所属的组织机构编号列表"
        /// <summary>
        /// 获取指定用户所属的组织机构编号列表
        /// </summary>
        /// <param name="user">用户数据</param>
        /// <param name="cancellationToken">用于传播应取消操作的通知</param>
        /// <returns>包含组织机构编号集合的<see cref="Task" />结果</returns>
        public async Task<IList<string>> GetOrganizationsAsync(TUser user, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            IList<string> list = _userOrganizationRepository.QueryAsNoTracking(m => Equals(m.UserId, user.Id)).Select(m => m.OrganizationId.ToString()).ToList();
            return await Task.FromResult(list);
        }
        #endregion
        #region "返回一个标志，指示指定的用户是否为给定组织机构的成员"
        /// <summary>
        /// 返回一个标志，指示指定的用户是否为给定组织机构的成员
        /// </summary>
        /// <param name="user">用户数据</param>
        /// <param name="organizationId">组织机构编号</param>
        /// <param name="cancellationToken">用于传播应取消操作的通知</param>
        /// <returns>包含布尔值的<see cref="Task" />结果</returns>
        public async Task<bool> IsInOrganizationAsync(TUser user, string organizationId, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNull(user, nameof(user));

            bool exist = _userOrganizationRepository.QueryAsNoTracking(m => Equals(m.UserId, user.Id) && Equals(m.OrganizationId, organizationId.CastTo<TOrganizationKey>())).Any();
            return await Task.FromResult(exist);
        }
        #endregion
        #region "返回作为组织机构成员的用户列表"
        /// <summary>
        /// 返回作为组织机构成员的用户列表
        /// </summary>
        /// <param name="organizationId">组织机构编号</param>
        /// <param name="cancellationToken">用于传播应取消操作的通知</param>
        /// <returns>包含用户集合的<see cref="Task" />结果</returns>
        public virtual async Task<IList<TUser>> GetUsersInOrganizationAsync(string organizationId, CancellationToken cancellationToken = new CancellationToken())
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            Check.NotNullOrEmpty(organizationId, nameof(organizationId));

            TOrganizationKey organizationKey = _organizationRepository.QueryAsNoTracking(m => Equals(m.Id, organizationId.CastTo<TOrganizationKey>())).Select(m => m.Id).FirstOrDefault();
            if (Equals(organizationKey, default(TOrganizationKey)))
            {
                throw new InvalidOperationException(I18N.T("组织机构 {0} 不存在", organizationId));
            }
            List<TUserKey> userIds = _userOrganizationRepository.QueryAsNoTracking(m => Equals(m.OrganizationId, organizationKey)).Select(m => m.UserId).ToList();
            IList<TUser> users = _userRepository.QueryAsNoTracking(m => userIds.Contains(m.Id)).ToList();
            return await Task.FromResult(users);
        }
        #endregion

        #endregion

        #region "方法 - Other"
        #region "Converts the provided <paramref name="id"/> to a strongly typed key object."
        /// <summary>
        /// Converts the provided <paramref name="id"/> to a strongly typed key object.
        /// </summary>
        /// <param name="id">The id to convert.</param>
        /// <returns>An instance of <typeparamref name="TUserKey"/> representing the provided <paramref name="id"/>.</returns>
        public virtual TUserKey ConvertIdFromString(string id)
        {
            if (id == null)
            {
                return default;
            }
            return (TUserKey)TypeDescriptor.GetConverter(typeof(TUserKey)).ConvertFromInvariantString(id);
        }
        #endregion
        #region "Converts the provided <paramref name="id"/> to its string representation."
        /// <summary>
        /// Converts the provided <paramref name="id"/> to its string representation.
        /// </summary>
        /// <param name="id">The id to convert.</param>
        /// <returns>An <see cref="string"/> representation of the provided <paramref name="id"/>.</returns>
        public virtual string ConvertIdToString(TUserKey id)
        {
            if (id.Equals(default))
            {
                return null;
            }
            return id.ToString();
        }
        #endregion
        #region "如果已释放，则抛出异常"
        /// <summary>
        /// 如果已释放，则抛出异常
        /// </summary>
        protected virtual void ThrowIfDisposed()
        {
            if (Disposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
        }
        #endregion

        #endregion

    }

}
